package com.tfswx.ruleengine.compute.util.sm4;

import java.io.BufferedInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

public abstract class DecorateInputStreamBase extends FilterInputStream {

    private int readSize;
    private int writeSize;

    private DecorateInputStreamBase(InputStream in) {
        super(packInputStream(in));
    }

    protected DecorateInputStreamBase(InputStream in, int readSize, int writeSize) {
        this(in);

        this.readSize = readSize;
        this.writeSize = writeSize;
        this.innerReadBuffer = new byte[this.readSize];
        this.innerWriteBuffer = new byte[this.writeSize];
    }

    private static InputStream packInputStream(InputStream inputStream) {
        //        return inputStream instanceof FileInputStream
        //                || inputStream.getClass().getTypeName().equalsIgnoreCase("sun.nio.ch.ChannelInputStream")
        //                ? new BufferedInputStream(inputStream)
        //                : inputStream;
        return inputStream instanceof BufferedInputStream ? inputStream : new BufferedInputStream(inputStream);
    }

    private byte[] innerReadBuffer;

    private byte[] innerWriteBuffer;
    private volatile int innerWriteOffset;
    private volatile int innerWriteLength;

    private volatile boolean isStarted;
    private volatile boolean isClosed;

    private void initStart() {
        if (!isStarted) {
            isStarted = true;

            if (innerReadBuffer == null) {
                innerReadBuffer = new byte[readSize];
            }
            if (innerWriteBuffer == null) {
                innerWriteBuffer = new byte[writeSize];
            }

            innerOpen();
        }
    }

    @Override
    public int read(byte[] buffer, int offset, int length) throws IOException {

        initStart();

        /**
         * 缓冲区有数据则写入目标，并清空缓冲区
         *
         * 判断是否已经写够了
         *
         * 不够，则读取源流数据，并处理到缓冲区
         */

        // 剩余需要写入的长度
        int lastReadLength = length;
        // 写入偏移量
        int writeOffset = offset;
        while (true) {
            // 缓冲区有数据则写入目标
            if (innerWriteLength > 0) {
                if (lastReadLength >= innerWriteLength) {
                    // 写完，清空缓冲区
                    System.arraycopy(innerWriteBuffer, innerWriteOffset, buffer, writeOffset, innerWriteLength);
                    // 写目标位置更新
                    lastReadLength -= innerWriteLength;
                    writeOffset += innerWriteLength;
                    // 清空缓冲区
                    innerWriteOffset = 0;
                    innerWriteLength = 0;
                } else {
                    // 写不完，缓冲区有剩
                    // 单次写入多少变量
                    System.arraycopy(innerWriteBuffer, innerWriteOffset, buffer, writeOffset, lastReadLength);
                    // 缓冲区更新
                    innerWriteOffset += lastReadLength;
                    innerWriteLength -= lastReadLength;
                    return length;
                }
            }

            // 判断是否写够了
            if (lastReadLength == 0) {
                return length;
            }

            // 没写够，尝试从源再读数据出来，处理后放入缓冲区
            // 有可能没读够，但只要不是返回小于0，则就还有数据未读完
            int tmpReadingCount, tmpReadedCount = 0;
            while ((tmpReadingCount = in.read(innerReadBuffer, tmpReadedCount, innerReadBuffer.length - tmpReadedCount)) > -1) {
                tmpReadedCount += tmpReadingCount;
                if (tmpReadedCount >= innerReadBuffer.length) {
                    break;
                }
            }
            int innerReadLength = tmpReadedCount == 0 ? -1 : tmpReadedCount;
            innerWriteLength = innerReadTo(innerReadBuffer, innerReadLength, innerWriteBuffer);
            if (innerWriteLength <= 0) {
                innerWriteLength = 0;
                // 源没有数据了
                int trueReadLength = writeOffset - offset;
                if (trueReadLength > 0) {
                    return trueReadLength;
                } else {
                    return -1;
                }
            }

            // 进入下次循环继续处理
        }
    }

    /**
     * 读单个字节位的辅助数组缓冲区
     */
    private final byte[] oneBytes = new byte[1];

    @Override
    public int read() throws IOException {

        initStart();

        int readCount = read(oneBytes, 0, 1);
        if (readCount < 0) {
            return -1;
        } else {
            return oneBytes[0];
        }
    }

    /**
     * 流开始读取处理方法
     */
    protected void innerOpen() {

    }

    /**
     * 流结束读取处理方法
     */
    protected void innerClose() {

    }

    /**
     * 内部实际写操作
     *
     * @param sourceBuffer       读分片数据缓冲区
     * @param sourceBufferLength 读分片数据缓冲区真实数据长度（读到末尾时会存在 -1 的情况，需要按需处理）
     * @param destBuffer         写入缓冲区
     * @return 实际写入 destBuffer 的数据长度（返回 -1 时表示没有数据需要写了）
     */
    protected abstract int innerReadTo(byte[] sourceBuffer, int sourceBufferLength, byte[] destBuffer);

    @Override
    public int available() throws IOException {
        return super.available() + innerWriteLength;
    }

    @Override
    public void close() throws IOException {
        try {
            super.close();
        } finally {
            if (!isClosed) {
                isClosed = true;
                innerClose();
            }
        }
    }

    @Override
    public final boolean markSupported() {
        return false;
    }

    /**
     * 跳过方法缓冲区大小
     */
    public final static int SKIP_BUFFER_SIZE = 4096;

    @Override
    public final long skip(long n) throws IOException {
        if (n <= 0) {
            return 0;
        }
        long least = n;
        long skipCount = 0;
        long readStepCount;
        byte[] nothing = new byte[SKIP_BUFFER_SIZE];
        for (; ; ) {
            if (least >= SKIP_BUFFER_SIZE) {
                readStepCount = read(nothing, 0, SKIP_BUFFER_SIZE);
            } else {
                readStepCount = read(nothing, 0, (int) least);
            }

            if (readStepCount == -1) {
                return skipCount;
            } else if (readStepCount == 0) {
                continue;
            } else { // readStepCount > 0
                skipCount += readStepCount;
                if (least > readStepCount) {
                    least -= readStepCount;
                } else {
                    break;
                }
            }
        }

        return skipCount;
    }

    @Override
    public final void mark(int readLimit) {
        throw new RuntimeException("不支持的操作");
    }

    @Override
    public final void reset() {
        throw new RuntimeException("不支持的操作");
    }
}
